#include "SQLSubqueryTableSource.h"
#include "SQLSelectQueryBlock.h"
#include "SQLUnionQuery.h"

#include "../../../utils/instanceof.h"
#include "../../visitor/SQLASTVisitor.h"

SETCALSS(SQLSubqueryTableSource);

SQLSubqueryTableSource::SQLSubqueryTableSource()
{
  columns = std::make_shared<std::list<SQLName_ptr>>();
}

SQLSubqueryTableSource::SQLSubqueryTableSource(string_ptr alias)
    : SQLTableSourceImpl(alias)
{
  columns = std::make_shared<std::list<SQLName_ptr>>();
  //
}

SQLSubqueryTableSource::SQLSubqueryTableSource(SQLSelect_ptr select, string_ptr alias)
    : SQLTableSourceImpl(alias)
{
  columns = std::make_shared<std::list<SQLName_ptr>>();
  // super(alias);
  this->setSelect(select);
}

SQLSubqueryTableSource::SQLSubqueryTableSource(SQLSelect_ptr select)
{
  columns = std::make_shared<std::list<SQLName_ptr>>();
  this->setSelect(select);
}

SQLSubqueryTableSource::SQLSubqueryTableSource(SQLSelectQuery_ptr query)
{
  SQLSubqueryTableSource(SQLSelect_ptr(new SQLSelect(query)));
}

SQLSubqueryTableSource::SQLSubqueryTableSource(SQLSelectQuery_ptr query, string_ptr alias)
{
  SQLSubqueryTableSource(SQLSelect_ptr(new SQLSelect(query)), alias);
}

SQLSelect_ptr SQLSubqueryTableSource::getSelect()
{
  return this->select;
}

void SQLSubqueryTableSource::setSelect(SQLSelect_ptr x)
{
  if (x != NULL)
  {
    x->setParent(SharedObject(SQLSubqueryTableSource));
  }
  this->select = x;
}

void SQLSubqueryTableSource::accept0(SQLASTVisitor_ptr visitor)
{
  if (visitor->visit(SharedObject(SQLSubqueryTableSource)))
  {
    if (select != NULL)
    {
      select->accept(visitor);
    }
  }
  visitor->endVisit(SharedObject(SQLSubqueryTableSource));
}

void SQLSubqueryTableSource::cloneTo(SQLSubqueryTableSource_ptr x)
{
  x->alias = alias;

  if (select != NULL)
  {
    x->select = std::dynamic_pointer_cast<SQLSelect>(select->clone());
    x->select->setParent(x);
  }

  for (SQLName_ptr column : *columns)
  {
    SQLName_ptr c2 = std::dynamic_pointer_cast<SQLName>(column->clone());
    // c2->setParent(x);
    x->columns->push_back(c2);
  }
}

// SQLSubqueryTableSource *SQLSubqueryTableSource::clone()
SQLObject_ptr SQLSubqueryTableSource::clone()
{
  SQLObject_ptr clone_tmp = std::shared_ptr<SQLSubqueryTableSource>(new SQLSubqueryTableSource());
  std::shared_ptr<SQLSubqueryTableSource> x = std::dynamic_pointer_cast<SQLSubqueryTableSource>(clone_tmp);
  cloneTo(x);
  return x;
}

SQLTableSource_ptr SQLSubqueryTableSource::findTableSourceWithColumn(string_ptr columnName)
{
  if (select == NULL)
  {
    return NULL;
  }

  // SQLSelectQueryBlock* queryBlock = select->getFirstQueryBlock();
  SQLSelectQueryBlock_ptr queryBlock = std::dynamic_pointer_cast<SQLSelectQueryBlock>(select->getQuery());
  if (queryBlock == NULL)
  {
    return NULL;
  }

  if (queryBlock->findSelectItem(columnName) != NULL)
  {
    return SharedObject(SQLSubqueryTableSource);
  }

  return NULL;
}

SQLTableSource_ptr SQLSubqueryTableSource::findTableSourceWithColumn(long columnNameHash, string_ptr columnName, int option)
{
  if (select == NULL)
  {
    return NULL;
  }

  // SQLSelectQueryBlock* queryBlock = select->getFirstQueryBlock();
  SQLSelectQueryBlock_ptr queryBlock = std::dynamic_pointer_cast<SQLSelectQueryBlock>(select->getQuery());
  if (queryBlock == NULL)
  {
    return NULL;
  }

  if (queryBlock->findSelectItem(columnNameHash) != NULL)
  {
    return SharedObject(SQLSubqueryTableSource);
  }

  return NULL;
}

bool SQLSubqueryTableSource::equals(Object_ptr o)
{
  if (this == o.get())
  {
    return true;
  }
  if (o == NULL || getClass() != o->getClass())
  {
    return false;
  }

  std::shared_ptr<SQLSubqueryTableSource> that = std::dynamic_pointer_cast<SQLSubqueryTableSource>(o);

  return select != NULL ? select->equals(that->select) : that->select == NULL;
}

int SQLSubqueryTableSource::hashCode()
{
  return select != NULL ? select->hashCode() : 0;
}

SQLName_list_ptr SQLSubqueryTableSource::getColumns()
{
  return columns;
}

void SQLSubqueryTableSource::addColumn(SQLName_ptr column)
{
  // column->setParent(this);
  this->columns->push_back(column);
}

// SQLColumnDefinition* SQLSubqueryTableSource::findColumn(long columnNameHash) {
//     // SQLSelectQueryBlock* queryBlock = select->getFirstQueryBlock();
//     SQLSelectQueryBlock* queryBlock = dynamic_cast<SQLSelectQueryBlock *>(select->getQuery());
//     if (queryBlock != NULL) {
//         return queryBlock->findColumn(columnNameHash);
//     } else {
//         if (instanceof <SQLSelectQuery , SQLUnionQuery>(select->getQuery()) && instanceof <SQLSelectQueryBlock  , SQLSelectQueryBlock>((dynamic_cast<SQLUnionQuery *>(select->getQuery()))->getFirstQueryBlock())) {
//             SQLSelectQueryBlock* left = (dynamic_cast<SQLUnionQuery *>(select->getQuery()))->getFirstQueryBlock();
//             return (dynamic_cast<SQLSelectQueryBlock *>(left))->findColumn(columnNameHash);
//         }
//     }
//     return NULL;
// }
